﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.UI;
using Coleotrack.Core.Helper;
using Coleotrack.Data;
using Coleotrack.Models;
using Coleotrack.Resources;
using Coleotrack.Service;
using Coleotrack.Service.Contracts;
using Coleotrack.Web;
using log4net;
using log4net.Core;

namespace Coleotrack.Controllers
{
    [IfInstalled]
    public class AccountController : Controller
    {
        private readonly ILog logger;

        public IFormsAuthentication FormsAuth
        {
            get;
            private set;
        }

        public IMembershipService MembershipService
        {
            get;
            private set;
        }

        public IUserService UserService
        {
            get;
            private set;
        }

        public AccountController()
        {
            
        }

        public AccountController(IFormsAuthentication formsAuth, IMembershipService service, IUserService userService)
        {
            FormsAuth = formsAuth;
            MembershipService = service;
            UserService = userService;
            logger = LogManager.GetLogger(typeof (AccountController));
        }

        [HttpGet]
        public ActionResult LogOn()
        {
            return View();
        }

        public ActionResult LogOff()
        {
            FormsAuth.SignOut();

            return RedirectToAction("Index", "Home");
        }

        [HttpPost]
        public ActionResult LogOn(LogOnViewModel model)
        {
            if (!ValidateLogon(model))
            {
                return View();
            }

            FormsAuth.SignIn(model.Username, true);

            if (!string.IsNullOrEmpty(model.ReturnUrl))
            {
                return Redirect(model.ReturnUrl);
            }
                
            return RedirectToAction("Index", "Home");
        }

        [HttpGet]
        public ActionResult Register()
        {
            return View("Register");
        }

        [HttpPost]
        public ActionResult Register(RegisterViewModel model)
        {
            // validate
            if(!ValidateRegisterModel(model))
            {
                return View("Register");
            }
            // create
            var user = CreateAccountFromModel(model);
            if(user == null)
            {
                return View("Register");
            }
            // send activation data
            string baseUrl = string.Format("{0}://{1}", Request.Url.Scheme, Request.Url.Authority);
            string relativeUrl = Url.Action("Activate", "Account", new {username = user.Name, activationCode = user.ActivationCode});
            string activationLink = string.Format("{0}{1}", baseUrl, relativeUrl);
#if DEBUG
            ViewData["activationLink"] = activationLink;
#endif
            //TODO: really send mail
            // show success
            return View("AccountCreated", user);
            //return RedirectToAction("RegistrationCompleted", "Account", new {id = user.Id});
        }

        private User CreateAccountFromModel(RegisterViewModel model)
        {
            var user = new User
                           {
                               Active = false,
                               Email = model.Email,
                               Name = model.Username,
                               Password = model.Password.Md5(),
                               Created = DateTime.Now
                           };
            try
            {
                UserService.Insert(user);
                // load changed data
                string name = user.Name;
                user = UserService.GetByName(name);
                if (user == null)
                {
                    throw new Exception(string.Format("User doesn't exist (Name: {0})", name));
                }
            }
            catch (Exception)
            {
                ModelState.AddModelError("Registration", labels.Registration_Account_Creation_Failed);
                return null;
            }
            return user;
        }

        [HttpGet, Authorize]
        public ActionResult Profile()
        {
            try
            {
                User u = UserService.GetByNameOrEmail(User.Identity.Name);

                return View(u.Profile);
            }
            catch (Exception ex)
            {
                return View("ProfileNotFound");
            }
        }

        [HttpPost, Authorize]
        public ActionResult Profile(UserProfile profile)
        {
            try
            {
                User u = UserService.GetByNameOrEmail(User.Identity.Name);

                if (u.Profile == null)
                {
                    u.Profile = new UserProfile();
                }

                u.Profile.FirstName = profile.FirstName;
                u.Profile.LastName = profile.LastName;

                //UserService.Save(u);

                return RedirectToAction("Index", "Home");
            }
            catch (Exception ex)
            {
                return View("ProfileNotFound");
            }
        }

        [HttpGet, Authorize]
        public ActionResult ChangePassword()
        {
            return View();
        }

        private bool ValidateRegisterModel(RegisterViewModel model)
        {
            if (!model.Username.IsNotEmptyText())
            {
                ModelState.AddModelError("Username", labels.Validation_Username);
            }

            if (!model.Email.IsValidEmailAddress())
            {
                ModelState.AddModelError("Email", labels.Validation_Email);
            }
            else if (!model.Email.Equals(model.EmailConfirmation))
            {
                ModelState.AddModelError("EmailConfirmation", labels.Validation_Email_Confirmation);
            }

            if (!model.Password.IsNotEmptyText())
            {
                ModelState.AddModelError("Password", labels.Validation_Password);
            }
            else if (!model.Password.Equals(model.PasswordConfirmation))
            {
                ModelState.AddModelError("PasswordConfirmation", labels.Validation_Password_Confirmation);
            }

            return ModelState.IsValid;
        }

        private bool ValidateLogon(LogOnViewModel model)
        {
            if (string.IsNullOrEmpty(model.Username))
            {
                ModelState.AddModelError("Username", labels.Validation_Username);
            }

            if (string.IsNullOrEmpty(model.Password))
            {
                ModelState.AddModelError("Password", labels.Validation_Password);
            }

            if (!MembershipService.ValidateUser(model.Username, model.Password.Md5()))
            {
                ModelState.AddModelError("_FORM", labels.Validation_LogOn);
            }

            return ModelState.IsValid;
        }

        [HttpPost, Authorize]
        public ActionResult ChangePassword(ChangePasswordViewModel model)
        {
            if (model.NewPassword == null)
            {
                ModelState.AddModelError("NewPassword", "Please specify a new password");
                return View();
            }

            if (string.IsNullOrEmpty(model.NewPassword.Trim()))
            {
                ModelState.AddModelError("NewPassword", "New password is empty");
                return View();
            }

            if (!model.NewPassword.Equals(model.NewPasswordConfirmation))
            {
                ModelState.AddModelError("NewPassword", "Password and password confirmation does not match");
                return View();
            }

            if (model.NewPassword.Length < MembershipService.MinPasswordLength)
            {
                ModelState.AddModelError("NewPassword", string.Format("New password is too short (must be at least {0} characters)", MembershipService.MinPasswordLength));
                return View();
            }

            if (!MembershipService.ValidateUser(User.Identity.Name, model.Password.Md5()))
            {
                ModelState.AddModelError("Password", "You entered wrong password");
                return View();
            }

            MembershipService.ChangePassword(User.Identity.Name, model.Password.Md5(), model.NewPassword.Md5());

            return RedirectToAction("Index", "Home");
        }

        public ActionResult Activate(string username, string activationCode)
        {
            try
            {
                var user = UserService.GetByName(username);

                if (!user.Active && user.ActivationCode.Equals(activationCode))
                {
                    user.Active = true;
                    //UserService.Save(user);
                    return View("ActivationSucceeded");
                }
                return View("ActivationFailed");
            }
            catch (Exception ex)
            {
                return View("ProfileNotFound"); 
            }
        }

        public ActionResult ForgotPassword()
        {
            return View("ForgotPassword");
        }

        [HttpPost]
        public ActionResult ForgotPassword(ForgotPasswordViewModel model)
        {
            User u;
            try
            {
                u = UserService.GetByNameOrEmail(model.UsernameEmail);
            }
            catch(InvalidOperationException)
            {
                // no matching item
                ModelState.AddModelError("UsernameEmail", labels.ForgotPassword_AccountDoesntExist);
                return View("ForgotPassword");
            }
            var r = new Random();
            string token = r.Next(int.MaxValue).ToString().Md5();
            u.PasswordRequestCode = string.Format("{0}/{1}", u.ActivationCode, token).Md5();
            //UserService.Save(u);
            // send request data
            string baseUrl = string.Format("{0}://{1}", Request.Url.Scheme, Request.Url.Authority);
            string relativeUrl = Url.Action("PasswordRequest", "Account", new { username = u.Name, token = token });
            string requestLink = string.Format("{0}{1}", baseUrl, relativeUrl);
#if DEBUG
            ViewData["requestLink"] = requestLink;
#endif
            return View("PasswordRequestSent");
        }

        public ActionResult PasswordRequest(string username, string token)
        {
            User u;
            try
            {
                u = UserService.GetByNameOrEmail(username);
            }
            catch (InvalidOperationException)
            {
                // no matching item
                return View("ProfileNotFound");
            }
            string requestCode = string.Format("{0}/{1}", u.ActivationCode, token).Md5();
            if(u.IsPasswordRequested() && u.PasswordRequestCode.Equals(requestCode))
            {
                return View("NewPasswordForm");
            }
            return View("InvalidPasswordRequest");
        }
    }
}